/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { AbstractInvokeExpr, ANONYMOUS_METHOD_PREFIX, ArkAssignStmt, ArkInstanceInvokeExpr, ArkMethod, ImportInfo, Local, Stmt } from 'arkanalyzer/lib';
import Logger, { LOG_MODULE_TYPE } from 'arkanalyzer/lib/utils/logger';
import { BaseChecker, BaseMetaData } from '../BaseChecker';
import { Rule, Defects, MatcherTypes, MethodMatcher, MatcherCallback, CheckerUtils } from '../../Index';
import { IssueReport } from '../../model/Defects';

const logger = Logger.getLogger(LOG_MODULE_TYPE.HOMECHECK, 'BadDeepCloneCheck');
const LODASH_CLONE_DEEP_FUNCTIONS = [
    '_.cloneDeep',
    'lodash.cloneDeep'
];
const JSON_PARSE_SIGNATURES: string[] = [
    `@built-in/lib.es5.d.ts: JSON.parse(string, @built-in/lib.es5.d.ts: JSON.${ANONYMOUS_METHOD_PREFIX}0(any, string, any))`,
    `@built-in/es5.d.ts: JSON.parse(string, @built-in/es5.d.ts: JSON.${ANONYMOUS_METHOD_PREFIX}0(any, string, any))`
];
const JSON_STRINGIFY_SIGNATURES: string[] = [
    `@built-in/lib.es5.d.ts: JSON.stringify(any, @built-in/lib.es5.d.ts: JSON.${ANONYMOUS_METHOD_PREFIX}1(any, string, any), string|number)`,
    `@built-in/es5.d.ts: JSON.stringify(any, @built-in/es5.d.ts: JSON.${ANONYMOUS_METHOD_PREFIX}1(any, string, any), string|number)`
];
const gMetaData: BaseMetaData = {
    severity: 1,
    ruleDocPath: 'docs/bad-deep-clone-check.md',
    description: 'Prioritize structuredClone for deep clone operations.'
};

interface RuleOptions {
    functions?: string[];
}

interface CloneFunction {
    objectName: string | null;
    methodName: string;
}

export class BadDeepCloneCheck implements BaseChecker {
    readonly metaData: BaseMetaData = gMetaData;
    public rule: Rule;
    public defects: Defects[] = [];
    public issues: IssueReport[] = [];

    private mtdMatcher: MethodMatcher = {
        matcherType: MatcherTypes.METHOD,
    };

    public registerMatchers(): MatcherCallback[] {
        const matchMethodCb: MatcherCallback = {
            matcher: this.mtdMatcher,
            callback: this.check
        };
        return [matchMethodCb];
    }

    public check = (target: ArkMethod): void => {
        const configFunctions = (this.rule.option[0] as RuleOptions | undefined)?.functions ?? [];
        const importInfos = target.getDeclaringArkFile().getImportInfos();
        const stmts = target.getBody()?.getCfg().getStmts() ?? [];
        // 处理导入信息和函数配置
        const importNames: string[] = this.extractImportNames(importInfos);
        const customFunctions: CloneFunction[] = this.parseFunctionNames(configFunctions);
        const lodashFunctions: CloneFunction[] = this.parseFunctionNames(LODASH_CLONE_DEEP_FUNCTIONS);
        // 检查每个语句
        for (let stmt of stmts) {
            const invokeExpr = CheckerUtils.getInvokeExprFromStmt(stmt);
            if (!invokeExpr) {
                continue;
            }
            const methodSignStr = invokeExpr.getMethodSignature().toString();
            // 检查JSON.parse和JSON.stringify组合
            if (JSON_PARSE_SIGNATURES.includes(methodSignStr)) {
                this.checkJsonClone(stmt, invokeExpr);
            } else {
                // 检查lodash克隆函数
                this.checkCloneFunction(stmt, invokeExpr, lodashFunctions, importNames, true);
                // 检查自定义克隆函数
                this.checkCloneFunction(stmt, invokeExpr, customFunctions, importNames, false);
            }
        }
    };

    /**
    * 从导入信息中提取导入的名称
    */
    private extractImportNames(importInfos: ImportInfo[]): string[] {
        return importInfos.map(info => info.getImportClauseName());
    }

    /**
     * 解析函数名列表为对象方法结构
     */
    private parseFunctionNames(functionNames: string[]): CloneFunction[] {
        return functionNames.map(funcName => {
            if (funcName.includes('.')) {
                const [objectName, methodName] = funcName.split('.', 2);
                return { objectName, methodName };
            }
            return { objectName: null, methodName: funcName };
        });
    }

    /**
     * 检查JSON.parse和JSON.stringify组合的深克隆
     */
    private checkJsonClone(stmt: Stmt, invokeExpr: AbstractInvokeExpr): void {
        const arg = invokeExpr.getArg(0);
        if (!arg) {
            return;
        }
        if (!(arg instanceof Local)) {
            return;
        }
        const declaringStmt = arg.getDeclaringStmt();
        if (!(declaringStmt instanceof ArkAssignStmt)) {
            return;
        }
        const rightOp = declaringStmt.getRightOp();
        if (!(rightOp instanceof ArkInstanceInvokeExpr)) {
            return;
        }
        const methodSignature = rightOp.getMethodSignature().toString();
        if (!JSON_STRINGIFY_SIGNATURES.includes(methodSignature)) {
            return;
        }
        this.reportIssue(stmt, invokeExpr);
    }

    /**
     * 检查克隆函数调用
     */
    private checkCloneFunction(stmt: Stmt, invokeExpr: AbstractInvokeExpr, functions: CloneFunction[], importNames: string[], checkImport: boolean): void {
        const methodName = invokeExpr.getMethodSignature().getMethodSubSignature().getMethodName();
        for (let func of functions) {
            if (func.methodName !== methodName) {
                continue;
            }
            // 处理带对象名的函数调用
            if (func.objectName) {
                if (!(invokeExpr instanceof ArkInstanceInvokeExpr)) {
                    continue;
                }
                const baseName = invokeExpr.getBase().getName();
                if (func.objectName !== baseName) {
                    continue;
                }
                if (checkImport && !importNames.includes(baseName)) {
                    continue;
                }
            }
            this.reportIssue(stmt, invokeExpr);
            return;
        }
    }

    private reportIssue(stmt: Stmt, invokeExpr: AbstractInvokeExpr): void {
        // 获取严重级别,声明方法和文件信息
        const severity = this.rule.alert ?? this.metaData.severity;
        const declaringMethod = stmt.getCfg().getDeclaringMethod();
        const arkFile = declaringMethod.getDeclaringArkFile();
        if (!arkFile) {
            return;
        }
        // 获取源代码位置和文本信息
        const originPosition = stmt.getOperandOriginalPosition(invokeExpr);
        const originText = stmt.getOriginalText();
        // 如果源代码文本为空，则不继续处理
        if (!originText || originText.length === 0) {
            return;
        }
        // 如果无法获取位置信息，则不继续处理
        if (!originPosition) {
            return;
        }
        // 提取位置信息：行号、起始列号、结束列号
        const lineNum = originPosition.getFirstLine();
        const startColumn = originPosition.getFirstCol();
        const endColumn = originPosition.getLastCol();
        let defects = new Defects(lineNum, startColumn, endColumn, this.metaData.description, severity, this.rule.ruleId,
            arkFile.getFilePath(), this.metaData.ruleDocPath, true, false, false);
        this.issues.push(new IssueReport(defects, undefined));
    }
}